/*
*  Copyright (C) 2008-2009 Andrej Stepanchuk
*  Copyright (C) 2009-2010 Howard Chu
*
*  This file is part of librtmp.
*
*  librtmp is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as
*  published by the Free Software Foundation; either version 2.1,
*  or (at your option) any later version.
*
*  librtmp is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with librtmp see the file COPYING.  If not, write to
*  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*  Boston, MA  02110-1301, USA.
*  http://www.gnu.org/copyleft/lgpl.html
*/

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

#include "rtmp_sys.h"
#include "log.h"

#define MAX_PRINT_LEN	2048

RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR;

static int neednl;

static FILE *fmsg;

static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;

static const char *levels[] =
{
	"CRIT", "ERROR", "WARNING", "INFO",
	"DEBUG", "DEBUG2"
};

static void rtmp_log_default ( int level, const char *format, va_list vl )
{
	char str[MAX_PRINT_LEN] = "";

	vsnprintf ( str, MAX_PRINT_LEN - 1, format, vl );

	/* Filter out 'no-name' */
	if ( RTMP_debuglevel < RTMP_LOGALL && strstr ( str, "no-name" ) != NULL )
	{
		return;
	}

	if ( !fmsg )
	{
		fmsg = stderr;
	}

	if ( level <= RTMP_debuglevel )
	{
		if ( neednl )
		{
			putc ( '\n', fmsg );
			neednl = 0;
		}

		fprintf ( fmsg, "%s: %s\n", levels[level], str );
#ifdef _DEBUG
		fflush ( fmsg );
#endif
	}
}

void RTMP_LogSetOutput ( FILE *file )
{
	fmsg = file;
}

void RTMP_LogSetLevel ( RTMP_LogLevel level )
{
	RTMP_debuglevel = level;
}

void RTMP_LogSetCallback ( RTMP_LogCallback *cbp )
{
	cb = cbp;
}

RTMP_LogLevel RTMP_LogGetLevel()
{
	return RTMP_debuglevel;
}

void RTMP_Log ( int level, const char *format, ... )
{
	va_list args;
	va_start ( args, format );
	cb ( level, format, args );
	va_end ( args );
}

static const char hexdig[] = "0123456789abcdef";

void RTMP_LogHex ( int level, const uint8_t *data, unsigned long len )
{
	unsigned long i;
	char line[50], *ptr;

	if ( level > RTMP_debuglevel )
	{
		return;
	}

	ptr = line;

	for ( i = 0; i < len; i++ )
	{
		*ptr++ = hexdig[0x0f & ( data[i] >> 4 )];
		*ptr++ = hexdig[0x0f & data[i]];

		if ( ( i & 0x0f ) == 0x0f )
		{
			*ptr = '\0';
			ptr = line;
			RTMP_Log ( level, "%s", line );
		}
		else
		{
			*ptr++ = ' ';
		}
	}

	if ( i & 0x0f )
	{
		*ptr = '\0';
		RTMP_Log ( level, "%s", line );
	}
}

void RTMP_LogHexString ( int level, const uint8_t *data, unsigned long len )
{
#define BP_OFFSET 9
#define BP_GRAPH 60
#define BP_LEN	80
	char	line[BP_LEN];
	unsigned long i;

	if ( !data || level > RTMP_debuglevel )
	{
		return;
	}

	/* in case len is zero */
	line[0] = '\0';

	for ( i = 0 ; i < len ; i++ )
	{
		int n = i % 16;
		unsigned off;

		if ( !n )
		{
			if ( i )
			{
				RTMP_Log ( level, "%s", line );
			}

			memset ( line, ' ', sizeof ( line ) - 2 );
			line[sizeof ( line ) - 2] = '\0';

			off = i % 0x0ffffU;

			line[2] = hexdig[0x0f & ( off >> 12 )];
			line[3] = hexdig[0x0f & ( off >> 8 )];
			line[4] = hexdig[0x0f & ( off >> 4 )];
			line[5] = hexdig[0x0f & off];
			line[6] = ':';
		}

		off = BP_OFFSET + n * 3 + ( ( n >= 8 ) ? 1 : 0 );
		line[off] = hexdig[0x0f & ( data[i] >> 4 )];
		line[off + 1] = hexdig[0x0f & data[i]];

		off = BP_GRAPH + n + ( ( n >= 8 ) ? 1 : 0 );

		if ( isprint ( data[i] ) )
		{
			line[BP_GRAPH + n] = data[i];
		}
		else
		{
			line[BP_GRAPH + n] = '.';
		}
	}

	RTMP_Log ( level, "%s", line );
}

/* These should only be used by apps, never by the library itself */
void RTMP_LogPrintf ( const char *format, ... )
{
	char str[MAX_PRINT_LEN] = "";
	int len;
	va_list args;
	va_start ( args, format );
	len = vsnprintf ( str, MAX_PRINT_LEN - 1, format, args );
	va_end ( args );

	if ( RTMP_debuglevel == RTMP_LOGCRIT )
	{
		return;
	}

	if ( !fmsg )
	{
		fmsg = stderr;
	}

	if ( neednl )
	{
		putc ( '\n', fmsg );
		neednl = 0;
	}

	if ( len > MAX_PRINT_LEN - 1 )
	{
		len = MAX_PRINT_LEN - 1;
	}

	fprintf ( fmsg, "%s", str );

	if ( str[len - 1] == '\n' )
	{
		fflush ( fmsg );
	}
}

void RTMP_LogStatus ( const char *format, ... )
{
	char str[MAX_PRINT_LEN] = "";
	va_list args;
	va_start ( args, format );
	vsnprintf ( str, MAX_PRINT_LEN - 1, format, args );
	va_end ( args );

	if ( RTMP_debuglevel == RTMP_LOGCRIT )
	{
		return;
	}

	if ( !fmsg )
	{
		fmsg = stderr;
	}

	fprintf ( fmsg, "%s", str );
	fflush ( fmsg );
	neednl = 1;
}
